home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / file-tra / rdist-6.1 / rdist-6 / rdist-6.1.0-linuxpl2 / src / server.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-05  |  33.6 KB  |  1,604 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33. #ifndef lint
  34. static char RCSid[] = 
  35. "$Id: server.c,v 6.72 1994/04/26 16:58:53 mcooper Exp $";
  36.  
  37. static char sccsid[] = "@(#)server.c    5.3 (Berkeley) 6/7/86";
  38.  
  39. static char copyright[] =
  40. "@(#) Copyright (c) 1983 Regents of the University of California.\n\
  41.  All rights reserved.\n";
  42. #endif /* not lint */
  43.  
  44. /*
  45.  * Server routines
  46.  */
  47.  
  48. #include "defs.h"
  49.  
  50. char    tempname[sizeof _RDIST_TMP + 1]; /* Tmp file name */
  51. char    buf[BUFSIZ];        /* general purpose buffer */
  52. char    target[MAXPATHLEN];    /* target/source directory name */
  53. char    *ptarget;        /* pointer to end of target name */
  54. int    catname = 0;        /* cat name to target name */
  55. char    *sptarget[32];        /* stack of saved ptarget's for directories */
  56. char   *fromhost = NULL;    /* Client hostname */
  57. static long min_freespace = 0;    /* Minimium free space on a filesystem */
  58. static long min_freefiles = 0;    /* Minimium free # files on a filesystem */
  59. int    oumask;            /* Old umask */
  60.  
  61. /*
  62.  * Cat "string" onto the target buffer with error checking.
  63.  */
  64. static int cattarget(string)
  65.     char *string;
  66. {
  67.     if (strlen(string) + strlen(target) + 2 > sizeof(target)) {
  68.         message(MT_INFO, "target buffer is not large enough.");
  69.         return(-1);
  70.     }
  71.     if (!ptarget) {
  72.         message(MT_INFO, "NULL target pointer set.");
  73.         return(-10);
  74.     }
  75.  
  76.     (void) sprintf(ptarget, "/%s", string);
  77.  
  78.     return(0);
  79. }
  80.     
  81. /*
  82.  * Set uid and gid ownership of a file.
  83.  */
  84. static int setownership(file, fd, uid, gid)
  85.     char *file;
  86.     int fd;
  87.     UID_T uid;
  88.     GID_T gid;
  89. {
  90.     int status = -1;
  91.  
  92.     /*
  93.      * We assume only the Superuser can change uid ownership.
  94.      */
  95.     if (getuid() == 0) {
  96. #if    defined(HAVE_FCHOWN)
  97.         if (fd != -1)
  98.             status = fchown(fd, (CHOWN_UID_T) uid, 
  99.                     (CHOWN_GID_T) gid);
  100. #endif
  101.         if (status < 0)
  102.             status = chown(file, (CHOWN_UID_T) uid, 
  103.                        (CHOWN_GID_T) gid);
  104.  
  105.         if (status < 0) {
  106.             message(MT_NOTICE, "%s: chown %d.%d failed: %s", 
  107.                 target, uid, gid, SYSERR);
  108.             return(-1);
  109.         }
  110.     } else {
  111. #if    defined(HAVE_FCHOWN)
  112.         if (fd != -1)
  113.             status = fchown(fd, (CHOWN_UID_T) -1, 
  114.                     (CHOWN_GID_T) gid);
  115. #endif
  116.         if (status < 0)
  117.             status = chown(file, (CHOWN_UID_T) -1, 
  118.                        (CHOWN_GID_T) gid);
  119.  
  120.         if (status < 0) {
  121.             message(MT_NOTICE, "%s: chgrp %d failed: %s",
  122.                 target, gid, SYSERR);
  123.             return(-1);
  124.         }
  125.     }
  126.  
  127.     return(0);
  128. }
  129.  
  130. /*
  131.  * Set mode of a file
  132.  */
  133. static int setmode(file, fd, mode)
  134.     char *file;
  135.     int fd;
  136.     int mode;
  137. {
  138.     int status = -1;
  139.  
  140.     if (mode == -1)
  141.         return(0);
  142.  
  143. #if    defined(HAVE_FCHMOD)
  144.     if (fd != -1)
  145.         status = fchmod(fd, mode);
  146. #endif
  147.  
  148.     if (status < 0)
  149.         status = chmod(file, mode);
  150.  
  151.     if (status < 0) {
  152.         message(MT_NOTICE, "%s: chmod failed: %s", target, SYSERR);
  153.         return(-1);
  154.     }
  155.  
  156.     return(0);
  157. }
  158. /*
  159.  * Change owner, group and mode of file.
  160.  */
  161. static int fchog(fd, file, owner, group, mode)
  162.     int fd;
  163.     char *file, *owner, *group;
  164.     int mode;
  165. {
  166.     static struct group *gr = NULL;
  167.     extern char *locuser;
  168.     register int i;
  169.     UID_T uid;
  170.     GID_T gid;
  171.     GID_T primegid = (GID_T)-2;
  172.  
  173.     uid = userid;
  174.     if (userid == 0) {    /* running as root; take anything */
  175.         if (*owner == ':') {
  176.             uid = (UID_T) atoi(owner + 1);
  177.         } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
  178.             if ((pw = getpwnam(owner)) == NULL) {
  179.                 if (mode != -1 && IS_ON(mode, S_ISUID)) {
  180.                     message(MT_NOTICE,
  181.                   "%s: unknown login name \"%s\", clearing setuid",
  182.                         target, owner);
  183.                     mode &= ~S_ISUID;
  184.                     uid = 0;
  185.                 } else
  186.                     message(MT_NOTICE,
  187.                     "%s: unknown login name \"%s\"",
  188.                         target, owner);
  189.             } else
  190.                 uid = pw->pw_uid;
  191.         } else {
  192.             uid = pw->pw_uid;
  193.             primegid = pw->pw_gid;
  194.         }
  195.         if (*group == ':') {
  196.             gid = (GID_T) atoi(group + 1);
  197.             goto ok;
  198.         }
  199.     } else {    /* not root, setuid only if user==owner */
  200.         struct passwd *lupw;
  201.  
  202.         if (mode != -1) {
  203.             if (IS_ON(mode, S_ISUID) && 
  204.                 strcmp(locuser, owner) != 0)
  205.                 mode &= ~S_ISUID;
  206.             if (mode)
  207.                 mode &= ~S_ISVTX; /* and strip sticky too */
  208.         }
  209.  
  210.         if ((lupw = getpwnam(locuser)) != NULL)
  211.             primegid = lupw->pw_gid;
  212.     }
  213.  
  214.     gid = (GID_T) -1;
  215.     if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
  216.         if ((*group == ':' && 
  217.              (getgrgid(gid = atoi(group + 1)) == NULL))
  218.             || ((gr = (struct group *)getgrnam(group)) == NULL)) {
  219.             if (mode != -1 && IS_ON(mode, S_ISGID)) {
  220.                 message(MT_NOTICE, 
  221.                 "%s: unknown group \"%s\", clearing setgid",
  222.                     target, group);
  223.                 mode &= ~S_ISGID;
  224.             } else
  225.                 message(MT_NOTICE, 
  226.                     "%s: unknown group \"%s\"",
  227.                     target, group);
  228.         } else
  229.             gid = gr->gr_gid;
  230.     } else
  231.         gid = gr->gr_gid;
  232.  
  233.     if (userid && gid >= 0x7fff && gid != primegid) {
  234.         if (gr)
  235.             for (i = 0; gr->gr_mem[i] != NULL; i++)
  236.                 if (strcmp(locuser, gr->gr_mem[i]) == 0)
  237.                     goto ok;
  238.         if (mode != -1 && IS_ON(mode, S_ISGID)) {
  239.             message(MT_NOTICE, 
  240.                 "%s: user %s not in group %s, clearing setgid",
  241.                 target, locuser, group);
  242.             mode &= ~S_ISGID;
  243.         }
  244.         gid = -1;
  245.     }
  246. ok:
  247.     /*
  248.      * Set uid and gid ownership.  If that fails, strip setuid and
  249.      * setgid bits from mode.  Once ownership is set, successful
  250.      * or otherwise, set the new file mode.
  251.      */
  252.     if (setownership(file, fd, uid, gid) < 0) {
  253.         if (mode != -1 && IS_ON(mode, S_ISUID)) {
  254.             message(MT_NOTICE, 
  255.                 "%s: chown failed, clearing setuid", target);
  256.             mode &= ~S_ISUID;
  257.         }
  258.         if (mode != -1 && IS_ON(mode, S_ISGID)) {
  259.             message(MT_NOTICE, 
  260.                 "%s: chown failed, clearing setgid", target);
  261.             mode &= ~S_ISGID;
  262.         }
  263.     }
  264.     (void) setmode(file, fd, mode);
  265.  
  266.  
  267.     return(0);
  268. }
  269.  
  270. /*
  271.  * Remove a file or directory (recursively) and send back an acknowledge
  272.  * or an error message.
  273.  */
  274. static int removefile(statb)
  275.     struct stat *statb;
  276. {
  277.     DIR *d;
  278.     static DIRENTRY *dp;
  279.     register char *cp;
  280.     struct stat stb;
  281.     char *optarget;
  282.     int len, failures = 0;
  283.  
  284.     switch (statb->st_mode & S_IFMT) {
  285.     case S_IFREG:
  286.     case S_IFLNK:
  287.         if (unlink(target) < 0) {
  288.             if (errno == ETXTBSY) {
  289.                 message(MT_REMOTE|MT_NOTICE, 
  290.                     "%s: unlink failed: %s",
  291.                     target, SYSERR);
  292.                 return(0);
  293.             } else {
  294.                 error("%s: unlink failed: %s", target, SYSERR);
  295.                 return(-1);
  296.             }
  297.         }
  298.         goto removed;
  299.  
  300.     case S_IFDIR:
  301.         break;
  302.  
  303.     default:
  304.         error("%s: not a plain file", target);
  305.         return(-1);
  306.     }
  307.  
  308.     errno = 0;
  309.     if ((d = opendir(target)) == NULL) {
  310.         error("%s: opendir failed: %s", target, SYSERR);
  311.         return(-1);
  312.     }
  313.  
  314.     optarget = ptarget;
  315.     len = ptarget - target;
  316.     while (dp = readdir(d)) {
  317.         if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') ||
  318.             (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' &&
  319.              dp->d_name[1] == '.'))
  320.             continue;
  321.  
  322.         if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) {
  323.             message(MT_REMOTE|MT_WARNING, "%s/%s: Name too long", 
  324.                 target, dp->d_name);
  325.             continue;
  326.         }
  327.         ptarget = optarget;
  328.         *ptarget++ = '/';
  329.         cp = dp->d_name;;
  330.         while (*ptarget++ = *cp++)
  331.             ;
  332.         ptarget--;
  333.         if (lstat(target, &stb) < 0) {
  334.             message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s", 
  335.                 target, SYSERR);
  336.             continue;
  337.         }
  338.         if (removefile(&stb) < 0)
  339.             ++failures;
  340.     }
  341.     (void) closedir(d);
  342.     ptarget = optarget;
  343.     *ptarget = CNULL;
  344.  
  345.     if (failures)
  346.         return(-1);
  347.  
  348.     if (rmdir(target) < 0) {
  349.         error("%s: rmdir failed: %s", target, SYSERR);
  350.         return(-1);
  351.     }
  352. removed:
  353.     message(MT_CHANGE|MT_REMOTE, "%s: removed", target);
  354.     return(0);
  355. }
  356.  
  357. /*
  358.  * Check the current directory (initialized by the 'T' command to server())
  359.  * for extraneous files and remove them.
  360.  */
  361. static void doclean(cp)
  362.     register char *cp;
  363. {
  364.     DIR *d;
  365.     register DIRENTRY *dp;
  366.     struct stat stb;
  367.     char *optarget, *ep;
  368.     int len;
  369.     opt_t opts;
  370.  
  371.     opts = strtol(cp, &ep, 8);
  372.     if (*ep != CNULL) {
  373.         error("clean: options not delimited");
  374.         return;
  375.     }
  376.     if ((d = opendir(target)) == NULL) {
  377.         error("%s: opendir failed: %s", target, SYSERR);
  378.         return;
  379.     }
  380.     ack();
  381.  
  382.     optarget = ptarget;
  383.     len = ptarget - target;
  384.     while (dp = readdir(d)) {
  385.         if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') ||
  386.             (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' &&
  387.              dp->d_name[1] == '.'))
  388.             continue;
  389.  
  390.         if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) {
  391.             message(MT_REMOTE|MT_WARNING, "%s/%s: Name too long", 
  392.                 target, dp->d_name);
  393.             continue;
  394.         }
  395.         ptarget = optarget;
  396.         *ptarget++ = '/';
  397.         cp = dp->d_name;;
  398.         while (*ptarget++ = *cp++)
  399.             ;
  400.         ptarget--;
  401.         if (lstat(target, &stb) < 0) {
  402.             message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s", 
  403.                 target, SYSERR);
  404.             continue;
  405.         }
  406.  
  407.         (void) sendcmd(CC_QUERY, "%s", dp->d_name);
  408.         (void) remline(cp = buf, sizeof(buf), TRUE);
  409.  
  410.         if (*cp != CC_YES)
  411.             continue;
  412.  
  413.         if (IS_ON(opts, DO_VERIFY))
  414.             message(MT_REMOTE|MT_INFO, "%s: need to remove", 
  415.                 target);
  416.         else
  417.             (void) removefile(&stb);
  418.     }
  419.     (void) closedir(d);
  420.  
  421.     ptarget = optarget;
  422.     *ptarget = CNULL;
  423. }
  424.  
  425. /*
  426.  * Frontend to doclean().
  427.  */
  428. static void clean(cp)
  429.     register char *cp;
  430. {
  431.     doclean(cp);
  432.     (void) sendcmd(CC_END, NULL);
  433.     (void) response();
  434. }
  435.  
  436. /*
  437.  * Execute a shell command to handle special cases.
  438.  * We can't really set an alarm timeout here since we
  439.  * have no idea how long the command should take.
  440.  */
  441. static void dospecial(cmd)
  442.     char *cmd;
  443. {
  444.     runcommand(cmd);
  445. }
  446.  
  447. /*
  448.  * Do a special cmd command.  This differs from normal special
  449.  * commands in that it's done after an entire command has been updated.
  450.  * The list of updated target files is sent one at a time with RC_FILE
  451.  * commands.  Each one is added to an environment variable defined by
  452.  * E_FILES.  When an RC_COMMAND is finally received, the E_FILES variable
  453.  * is stuffed into our environment and a normal dospecial() command is run.
  454.  */
  455. static void docmdspecial()
  456. {
  457.     register char *cp;
  458.     char *cmd, *env = NULL;
  459.     int n;
  460.     int len;
  461.  
  462.     /* We're ready */
  463.     ack();
  464.  
  465.     for ( ; ; ) {
  466.         n = remline(cp = buf, sizeof(buf), FALSE);
  467.         if (n <= 0) {
  468.             error("cmdspecial: premature end of input.");
  469.             return;
  470.         }
  471.  
  472.         switch (*cp++) {
  473.         case RC_FILE:
  474.             if (env == NULL) {
  475.                 len = (2 * sizeof(E_FILES)) + strlen(cp) + 10;
  476.                 env = (char *) xmalloc(len);
  477.                 (void) sprintf(env, "export %s;%s=%s", 
  478.                            E_FILES, E_FILES, cp);
  479.             } else {
  480.                 len = strlen(env);
  481.                 env = (char *) xrealloc(env, 
  482.                             len + strlen(cp) + 2);
  483.                 env[len] = CNULL;
  484.                 (void) strcat(env, ":");
  485.                 (void) strcat(env, cp);
  486.             }
  487.             ack();
  488.             break;
  489.  
  490.         case RC_COMMAND:
  491.             if (env) {
  492.                 len = strlen(env);
  493.                 env = (char *) xrealloc(env, 
  494.                             len + strlen(cp) + 2);
  495.                 env[len] = CNULL;
  496.                 (void) strcat(env, ";");
  497.                 (void) strcat(env, cp);
  498.                 cmd = env;
  499.             } else
  500.                 cmd = cp;
  501.  
  502.             dospecial(cmd);
  503.             if (env)
  504.                 (void) free(env);
  505.             return;
  506.  
  507.         default:
  508.             error("Unknown cmdspecial command '%s'.", cp);
  509.             return;
  510.         }
  511.     }
  512. }
  513.  
  514. /*
  515.  * Query. Check to see if file exists. Return one of the following:
  516.  *
  517. #ifdef NFS_CHECK
  518.  *  QC_ONNFS        - resides on a NFS
  519. #endif NFS_CHECK
  520. #ifdef RO_CHECK
  521.  *  QC_ONRO        - resides on a Read-Only filesystem
  522. #endif RO_CHECK
  523.  *  QC_NO        - doesn't exist
  524.  *  QC_YESsize mtime     - exists and its a regular file (size & mtime of file)
  525.  *  QC_YES        - exists and its a directory or symbolic link
  526.  *  QC_ERRMSGmessage     - error message
  527.  */
  528. static void query(name)
  529.     char *name;
  530. {
  531.     static struct stat stb;
  532.     int s = -1, stbvalid = 0;
  533.  
  534.     if (catname && cattarget(name) < 0)
  535.         return;
  536.  
  537. #if    defined(NFS_CHECK)
  538.     if (IS_ON(options, DO_CHKNFS)) {
  539.         s = is_nfs_mounted(target, &stb, &stbvalid);
  540.         if (s > 0)
  541.             (void) sendcmd(QC_ONNFS, NULL);
  542.  
  543.         /* Either the above check was true or an error occured */
  544.         /* and is_nfs_mounted sent the error message */
  545.         if (s != 0) {
  546.             *ptarget = CNULL;
  547.             return;
  548.         }
  549.     }
  550. #endif     /* NFS_CHECK */
  551.  
  552. #if    defined(RO_CHECK)
  553.     if (IS_ON(options, DO_CHKREADONLY)) {
  554.         s = is_ro_mounted(target, &stb, &stbvalid);
  555.         if (s > 0)
  556.             (void) sendcmd(QC_ONRO, NULL);
  557.  
  558.         /* Either the above check was true or an error occured */
  559.         /* and is_ro_mounted sent the error message */
  560.         if (s != 0) {
  561.             *ptarget = CNULL;
  562.             return;
  563.         }
  564.     }
  565. #endif     /* RO_CHECK */
  566.  
  567.     if (IS_ON(options, DO_CHKSYM)) {
  568.         if (is_symlinked(target, &stb, &stbvalid) > 0) {
  569.             (void) sendcmd(QC_SYM, NULL);
  570.             return;
  571.         }
  572.     }
  573.  
  574.     /*
  575.      * If stbvalid is false, "stb" is not valid because:
  576.      *    a) RO_CHECK and NFS_CHECK were not defined
  577.      *    b) The stat by is_*_mounted() either failed or
  578.      *       does not match "target".
  579.      */
  580.     if (!stbvalid && lstat(target, &stb) < 0) {
  581.         if (errno == ENOENT)
  582.             (void) sendcmd(QC_NO, NULL);
  583.         else
  584.             error("%s: lstat failed: %s", target, SYSERR);
  585.         *ptarget = CNULL;
  586.         return;
  587.     }
  588.  
  589.     switch (stb.st_mode & S_IFMT) {
  590.     case S_IFLNK:
  591.     case S_IFDIR:
  592.     case S_IFREG:
  593.         (void) sendcmd(QC_YES, "%ld %ld %o %s %s",
  594.                    stb.st_size, stb.st_mtime, stb.st_mode & 07777,
  595.                    getusername(stb.st_uid, target, options), 
  596.                    getgroupname(stb.st_gid, target, options));
  597.         break;
  598.  
  599.     default:
  600.         error("%s: not a file or directory", target);
  601.         break;
  602.     }
  603.     *ptarget = CNULL;
  604. }
  605.  
  606. /*
  607.  * Check to see if parent directory exists and create one if not.
  608.  */
  609. static int chkparent(name, opts)
  610.     char *name;
  611.     opt_t opts;
  612. {
  613.     register char *cp;
  614.     struct stat stb;
  615.     int r = -1;
  616.  
  617.     debugmsg(DM_CALL, "chkparent(%s, %o) start\n", name, opts);
  618.  
  619.     cp = strrchr(name, '/');
  620.     if (cp == NULL || cp == name)
  621.         return(0);
  622.  
  623.     *cp = CNULL;
  624.  
  625.     if (lstat(name, &stb) < 0) {
  626.         if (errno == ENOENT && chkparent(name, opts) >= 0) {
  627.             if (mkdir(name, 0777 & ~oumask) == 0) {
  628.                 message(MT_NOTICE, "%s: mkdir", name);
  629.                 r = 0;
  630.             } else 
  631.                 debugmsg(DM_MISC, 
  632.                      "chkparent(%s, %o) mkdir fail: %s\n",
  633.                      name, opts, SYSERR);
  634.         }
  635.     } else    /* It exists */
  636.         r = 0;
  637.  
  638.     /* Put back what we took away */
  639.     *cp = '/';
  640.  
  641.     return(r);
  642. }
  643.  
  644. /*
  645.  * Save a copy of 'file' by renaming it.
  646.  */
  647. static char *savetarget(file)
  648.     char *file;
  649. {
  650.     static char savefile[MAXPATHLEN];
  651.  
  652.     if (strlen(file) + sizeof(SAVE_SUFFIX) + 1 > MAXPATHLEN) {
  653.         error("%s: Cannot save: Save name too long", file);
  654.         return((char *) NULL);
  655.     }
  656.  
  657.     (void) sprintf(savefile, "%s%s", file, SAVE_SUFFIX);
  658.  
  659.     if (unlink(savefile) != 0 && errno != ENOENT) {
  660.         message(MT_NOTICE, "%s: remove failed: %s", savefile, SYSERR);
  661.         return((char *) NULL);
  662.     }
  663.  
  664.     if (rename(file, savefile) != 0 && errno != ENOENT) {
  665.         error("%s -> %s: rename failed: %s", 
  666.               file, savefile, SYSERR);
  667.         return((char *) NULL);
  668.     }
  669.  
  670.     return(savefile);
  671. }
  672.  
  673. /*
  674.  * Receive a file
  675.  */
  676. static void recvfile(new, opts, mode, owner, group, mtime, atime, size)
  677.     /*ARGSUSED*/
  678.     char *new;
  679.     opt_t opts;
  680.     int mode;
  681.     char *owner, *group;
  682.     time_t mtime;
  683.     time_t atime;
  684.     off_t size;
  685. {
  686.     int f, wrerr, olderrno;
  687.     off_t i;
  688.     register char *cp;
  689.     char *savefile = NULL;
  690.  
  691.     /*
  692.      * Create temporary file
  693.      */
  694.     if ((f = creat(new, mode)) < 0) {
  695.         if (errno != ENOENT || chkparent(new, opts) < 0 ||
  696.             (f = creat(new, mode)) < 0) {
  697.             error("%s: create failed: %s", new, SYSERR);
  698.             (void) unlink(new);
  699.             return;
  700.         }
  701.     }
  702.  
  703.     /*
  704.      * Receive the file itself
  705.      */
  706.     ack();
  707.     wrerr = 0;
  708.     olderrno = 0;
  709.     for (i = 0; i < size; i += BUFSIZ) {
  710.         int amt = BUFSIZ;
  711.  
  712.         cp = buf;
  713.         if (i + amt > size)
  714.             amt = size - i;
  715.         do {
  716.             int j;
  717.  
  718.             j = readrem(cp, amt);
  719.             if (j <= 0) {
  720.                 (void) close(f);
  721.                 (void) unlink(new);
  722.                 fatalerr(
  723.                    "Read error occured while receiving file.");
  724.                 finish();
  725.             }
  726.             amt -= j;
  727.             cp += j;
  728.         } while (amt > 0);
  729.         amt = BUFSIZ;
  730.         if (i + amt > size)
  731.             amt = size - i;
  732.         if (wrerr == 0 && write(f, buf, amt) != amt) {
  733.             olderrno = errno;
  734.             wrerr++;
  735.         }
  736.     }
  737.  
  738.     if (response() < 0) {
  739.         (void) close(f);
  740.         (void) unlink(new);
  741.         return;
  742.     }
  743.     if (wrerr) {
  744.         error("%s: Write error: %s", new, strerror(olderrno));
  745.         (void) close(f);
  746.         (void) unlink(new);
  747.         return;
  748.     }
  749.  
  750.     /*
  751.      * Do file comparison if enabled
  752.      */
  753.     if (IS_ON(opts, DO_COMPARE)) {
  754.         FILE *f1, *f2;
  755.         int c;
  756.  
  757.         errno = 0;    /* fopen is not a syscall */
  758.         if ((f1 = fopen(target, "r")) == NULL) {
  759.             error("%s: open for read failed: %s", target, SYSERR);
  760.             (void) close(f);
  761.             (void) unlink(new);
  762.             return;
  763.         }
  764.         errno = 0;
  765.         if ((f2 = fopen(new, "r")) == NULL) {
  766.             error("%s: open for read failed: %s", new, SYSERR);
  767.             (void) close(f);
  768.             (void) unlink(new);
  769.             return;
  770.         }
  771.         while ((c = getc(f1)) == getc(f2))
  772.             if (c == EOF) {
  773.                 debugmsg(DM_MISC, 
  774.                      "Files are the same '%s' '%s'.",
  775.                      target, new);
  776.                 (void) fclose(f1);
  777.                 (void) fclose(f2);
  778.                 (void) close(f);
  779.                 (void) unlink(new);
  780.                 /*
  781.                  * This isn't an error per-se, but we
  782.                  * need to indicate to the master that
  783.                  * the file was not updated.
  784.                  */
  785.                 error("");
  786.                 return;
  787.             }
  788.         debugmsg(DM_MISC, "Files are different '%s' '%s'.",
  789.              target, new);
  790.         (void) fclose(f1);
  791.         (void) fclose(f2);
  792.         if (IS_ON(opts, DO_VERIFY)) {
  793.             message(MT_REMOTE|MT_INFO, "%s: need to update", 
  794.                 target);
  795.             (void) close(f);
  796.             (void) unlink(new);
  797.             return;
  798.         }
  799.     }
  800.  
  801.     /*
  802.      * Set owner, group, and file mode
  803.      */
  804.     if (fchog(f, new, owner, group, mode) < 0) {
  805.         (void) close(f);
  806.         (void) unlink(new);
  807.         return;
  808.     }
  809.     (void) close(f);
  810.  
  811.     /*
  812.      * Perform utimes() after file is closed to make
  813.      * certain OS's, such as NeXT 2.1, happy.
  814.      */
  815.     if (setfiletime(new, time((time_t *) 0), mtime) < 0)
  816.         message(MT_NOTICE, "%s: utimes failed: %s", new, SYSERR);
  817.  
  818.     /*
  819.      * Try to save target file from being over-written
  820.      */
  821.     if (IS_ON(opts, DO_SAVETARGETS))
  822.         if ((savefile = savetarget(target)) == NULL) {
  823.             (void) unlink(new);
  824.             return;
  825.         }
  826.  
  827.     /*
  828.      * Install new (temporary) file as the actual target
  829.      */
  830.     if (rename(new, target) < 0) {
  831.         /*
  832.          * If the rename failed due to "Text file busy", then
  833.          * try to rename the target file and retry the rename.
  834.          */
  835.         if (errno == ETXTBSY) {
  836.             /* Save the target */
  837.             if ((savefile = savetarget(target)) != NULL) {
  838.                 /* Retry installing new file as target */
  839.                 if (rename(new, target) < 0) {
  840.                     error("%s -> %s: rename failed: %s",
  841.                           new, target, SYSERR);
  842.                     /* Try to put back save file */
  843.                     if (rename(savefile, target) < 0)
  844.                         error(
  845.                              "%s -> %s: rename failed: %s",
  846.                               savefile, target, 
  847.                               SYSERR);
  848.                 } else
  849.                     message(MT_NOTICE, "%s: renamed to %s",
  850.                         target, savefile);
  851.             }
  852.         } else {
  853.             error("%s -> %s: rename failed: %s", 
  854.                   new, target, SYSERR);
  855.             (void) unlink(new);
  856.         }
  857.     }
  858.  
  859.     if (IS_ON(opts, DO_COMPARE))
  860.         message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
  861.     else
  862.         ack();
  863. }
  864.  
  865. /*
  866.  * Receive a directory
  867.  */
  868. static void recvdir(opts, mode, owner, group)
  869.     opt_t opts;
  870.     int mode;
  871.     char *owner, *group;
  872. {
  873.     static char lowner[100], lgroup[100];
  874.     register char *cp;
  875.     struct stat stb;
  876.     int s;
  877.  
  878.     s = lstat(target, &stb);
  879.     if (s == 0) {
  880.         /*
  881.          * If target is not a directory, remove it
  882.          */
  883.         if (!S_ISDIR(stb.st_mode)) {
  884.             if (IS_ON(opts, DO_VERIFY))
  885.                 message(MT_NOTICE, "%s: need to remove",
  886.                     target);
  887.             else {
  888.                 if (unlink(target) < 0) {
  889.                     error("%s: remove failed: %s",
  890.                           target, SYSERR);
  891.                     return;
  892.                 }
  893.             }
  894.             s = -1;
  895.             errno = ENOENT;
  896.         } else {
  897.             if (!IS_ON(opts, DO_NOCHKMODE) &&
  898.                 (stb.st_mode & 07777) != mode) {
  899.                 if (IS_ON(opts, DO_VERIFY))
  900.                     message(MT_NOTICE, 
  901.                         "%s: need to chmod to %o",
  902.                         target, mode);
  903.                 else {
  904.                     if (chmod(target, mode) != 0)
  905.                         message(MT_NOTICE,
  906.                       "%s: chmod from %o to %o failed: %s",
  907.                             target, 
  908.                             stb.st_mode & 07777, 
  909.                             mode,
  910.                             SYSERR);
  911.                     else
  912.                         message(MT_NOTICE,
  913.                         "%s: chmod from %o to %o",
  914.                             target, 
  915.                             stb.st_mode & 07777, 
  916.                             mode);
  917.                 }
  918.             }
  919.  
  920.             /*
  921.              * Check ownership and set if necessary
  922.              */
  923.             lowner[0] = CNULL;
  924.             lgroup[0] = CNULL;
  925.  
  926.             if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
  927.                 int o;
  928.  
  929.                 o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER :
  930.                     opts;
  931.                 if (cp = getusername(stb.st_uid, target, o))
  932.                     if (strcmp(owner, cp))
  933.                         (void) strcpy(lowner, cp);
  934.             }
  935.             if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
  936.                 int o;
  937.  
  938.                 o = (group[0] == ':') ? opts & DO_NUMCHKGROUP :
  939.                     opts;
  940.                 if (cp = getgroupname(stb.st_gid, target, o))
  941.                     if (strcmp(group, cp))
  942.                         (void) strcpy(lgroup, cp);
  943.             }
  944.  
  945.             /*
  946.              * Need to set owner and/or group
  947.              */
  948. #define PRN(n) ((n[0] == ':') ? n+1 : n)
  949.             if (lowner[0] != CNULL || lgroup[0] != CNULL) {
  950.                 if (lowner[0] == CNULL && 
  951.                     (cp = getusername(stb.st_uid, 
  952.                               target, opts)))
  953.                     (void) strcpy(lowner, cp);
  954.                 if (lgroup[0] == CNULL && 
  955.                     (cp = getgroupname(stb.st_gid, 
  956.                                target, opts)))
  957.                     (void) strcpy(lgroup, cp);
  958.  
  959.                 if (IS_ON(opts, DO_VERIFY))
  960.                     message(MT_NOTICE,
  961.                 "%s: need to chown from %s.%s to %s.%s",
  962.                         target, 
  963.                         PRN(lowner), PRN(lgroup),
  964.                         PRN(owner), PRN(group));
  965.                 else {
  966.                     if (fchog(-1, target, owner, 
  967.                           group, -1) == 0)
  968.                         message(MT_NOTICE,
  969.                            "%s: chown from %s.%s to %s.%s",
  970.                             target,
  971.                             PRN(lowner), 
  972.                             PRN(lgroup),
  973.                             PRN(owner), 
  974.                             PRN(group));
  975.                 }
  976.             }
  977. #undef PRN
  978.             ack();
  979.             return;
  980.         }
  981.     }
  982.  
  983.     if (IS_ON(opts, DO_VERIFY)) {
  984.         ack();
  985.         return;
  986.     }
  987.  
  988.     /*
  989.      * Create the directory
  990.      */
  991.     if (s < 0) {
  992.         if (errno == ENOENT) {
  993.             if (mkdir(target, mode) == 0 ||
  994.                 chkparent(target, opts) == 0 && 
  995.                 mkdir(target, mode) == 0) {
  996.                 message(MT_NOTICE, "%s: mkdir", target);
  997.                 (void) fchog(-1, target, owner, group, mode);
  998.                 ack();
  999.             } else {
  1000.                 error("%s: mkdir failed: %s", target, SYSERR);
  1001.                 ptarget = sptarget[--catname];
  1002.                 *ptarget = CNULL;
  1003.             }
  1004.             return;
  1005.         }
  1006.     }
  1007.     error("%s: lstat failed: %s", target, SYSERR);
  1008.     ptarget = sptarget[--catname];
  1009.     *ptarget = CNULL;
  1010. }
  1011.  
  1012. /*
  1013.  * Receive a link
  1014.  */
  1015. static void recvlink(new, opts, mode, size)
  1016.     char *new;
  1017.     opt_t opts;
  1018.     int mode;
  1019.     off_t size;
  1020. {
  1021.     struct stat stb;
  1022.     char *optarget;
  1023.     off_t i;
  1024.  
  1025.     /*
  1026.      * Read basic link info
  1027.      */
  1028.     ack();
  1029.     (void) remline(buf, sizeof(buf), TRUE);
  1030.  
  1031.     if (response() < 0) {
  1032.         err();
  1033.         return;
  1034.     }
  1035.  
  1036.     /*
  1037.      * Make new symlink using a temporary name
  1038.      */
  1039.     if (symlink(buf, new) < 0) {
  1040.         if (errno != ENOENT || chkparent(new, opts) < 0 ||
  1041.             symlink(buf, new) < 0) {
  1042.             error("%s -> %s: symlink failed: %s", new, buf,SYSERR);
  1043.             (void) unlink(new);
  1044.             return;
  1045.         }
  1046.     }
  1047.  
  1048.     /*
  1049.      * Do comparison of what link is pointing to if enabled
  1050.      */
  1051.     mode &= 0777;
  1052.     if (IS_ON(opts, DO_COMPARE)) {
  1053.         char tbuf[MAXPATHLEN];
  1054.         
  1055.         if ((i = readlink(target, tbuf, sizeof(tbuf))) >= 0 &&
  1056.             i == size && strncmp(buf, tbuf, (int) size) == 0) {
  1057.             (void) unlink(new);
  1058.             ack();
  1059.             return;
  1060.         }
  1061.         if (IS_ON(opts, DO_VERIFY)) {
  1062.             (void) unlink(new);
  1063.             message(MT_REMOTE|MT_INFO, "%s: need to update",
  1064.                 target);
  1065.             (void) sendcmd(C_END, NULL);
  1066.             (void) response();
  1067.             return;
  1068.         }
  1069.     }
  1070.  
  1071.     /*
  1072.      * See if target is a directory and remove it if it is
  1073.      */
  1074.     if (lstat(target, &stb) == 0) {
  1075.         if (S_ISDIR(stb.st_mode)) {
  1076.             optarget = ptarget;
  1077.             for (ptarget = target; *ptarget; ptarget++);
  1078.             if (removefile(&stb) < 0) {
  1079.                 ptarget = optarget;
  1080.                 (void) unlink(new);
  1081.                 (void) sendcmd(C_END, NULL);
  1082.                 (void) response();
  1083.                 return;
  1084.             }
  1085.             ptarget = optarget;
  1086.         }
  1087.     }
  1088.  
  1089.     /*
  1090.      * Install link as the target
  1091.      */
  1092.     if (rename(new, target) < 0) {
  1093.         error("%s -> %s: symlink rename failed: %s",
  1094.               new, target, SYSERR);
  1095.         (void) unlink(new);
  1096.         (void) sendcmd(C_END, NULL);
  1097.         (void) response();
  1098.         return;
  1099.     }
  1100.  
  1101.     if (IS_ON(opts, DO_COMPARE))
  1102.         message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
  1103.     else
  1104.             ack();
  1105.  
  1106.     /*
  1107.      * Indicate end of receive operation
  1108.      */
  1109.     (void) sendcmd(C_END, NULL);
  1110.     (void) response();
  1111. }
  1112.  
  1113. /*
  1114.  * Creat a hard link to existing file.
  1115.  */
  1116. static void hardlink(cmd)
  1117.     char *cmd;
  1118. {
  1119.     struct stat stb;
  1120.     int exists = 0;
  1121.     char *oldname, *newname;
  1122.     char *cp = cmd;
  1123.     static char expbuf[BUFSIZ];
  1124.  
  1125.     /* Skip over opts */
  1126.     (void) strtol(cp, &cp, 8);
  1127.     if (*cp++ != ' ') {
  1128.         error("hardlink: options not delimited");
  1129.         return;
  1130.     }
  1131.  
  1132.     oldname = strtok(cp, " ");
  1133.     if (oldname == NULL) {
  1134.         error("hardlink: oldname name not delimited");
  1135.         return;
  1136.     }
  1137.  
  1138.     newname = strtok((char *)NULL, " ");
  1139.     if (newname == NULL) {
  1140.         error("hardlink: new name not specified");
  1141.         return;
  1142.     }
  1143.  
  1144.     if (exptilde(expbuf, oldname) == NULL) {
  1145.         error("hardlink: tilde expansion failed");
  1146.         return;
  1147.     }
  1148.     oldname = expbuf;
  1149.  
  1150.     if (catname && cattarget(newname) < 0) {
  1151.         error("Cannot set newname target.");
  1152.         return;
  1153.     }
  1154.  
  1155.     if (lstat(target, &stb) == 0) {
  1156.         int mode = stb.st_mode & S_IFMT;
  1157.  
  1158.         if (mode != S_IFREG && mode != S_IFLNK) {
  1159.             error("%s: not a regular file", target);
  1160.             return;
  1161.         }
  1162.         exists = 1;
  1163.     }
  1164.  
  1165.     if (chkparent(target, options) < 0 ) {
  1166.         error("%s: no parent: %s ", target, SYSERR);
  1167.         return;
  1168.     }
  1169.     if (exists && (unlink(target) < 0)) {
  1170.         error("%s: unlink failed: %s", target, SYSERR);
  1171.         return;
  1172.     }
  1173.     if (link(oldname, target) < 0) {
  1174.         error("%s: cannot link to %s: %s", target, oldname, SYSERR);
  1175.         return;
  1176.     }
  1177.     ack();
  1178. }
  1179.  
  1180. /*
  1181.  * Set configuration information.
  1182.  *
  1183.  * A key letter is followed immediately by the value
  1184.  * to set.  The keys are:
  1185.  *    SC_FREESPACE    - Set minimium free space of filesystem
  1186.  *    SC_FREEFILES    - Set minimium free number of files of filesystem
  1187.  */
  1188. static void setconfig(cmd)
  1189.     char *cmd;
  1190. {
  1191.     register char *cp = cmd;
  1192.     char *estr;
  1193.  
  1194.     switch (*cp++) {
  1195.     case SC_HOSTNAME:    /* Set hostname */
  1196.         /*
  1197.          * Only use info if we don't know who this is.
  1198.          */
  1199.         if (!fromhost) {
  1200.             fromhost = strdup(cp);
  1201.             message(MT_SYSLOG, "startup for %s",  fromhost);
  1202. #if defined(SETARGS)
  1203.             setproctitle("serving %s", cp);
  1204. #endif /* SETARGS */
  1205.         }
  1206.         break;
  1207.  
  1208.     case SC_FREESPACE:     /* Minimium free space */
  1209.         if (!isdigit(*cp)) {
  1210.             fatalerr("Expected digit, got '%s'.", cp);
  1211.             return;
  1212.         }
  1213.         min_freespace = (unsigned long) atoi(cp);
  1214.         break;
  1215.  
  1216.     case SC_FREEFILES:     /* Minimium free files */
  1217.         if (!isdigit(*cp)) {
  1218.             fatalerr("Expected digit, got '%s'.", cp);
  1219.             return;
  1220.         }
  1221.         min_freefiles = (unsigned long) atoi(cp);
  1222.         break;
  1223.  
  1224.     case SC_LOGGING:    /* Logging options */
  1225.         if (estr = msgparseopts(cp, TRUE)) {
  1226.             fatalerr("Bad message option string (%s): %s", 
  1227.                  cp, estr);
  1228.             return;
  1229.         }
  1230.         break;
  1231.  
  1232.     default:
  1233.         message(MT_NOTICE, "Unknown config command \"%s\".", cp-1);
  1234.         return;
  1235.     }
  1236. }
  1237.  
  1238. /*
  1239.  * Receive something
  1240.  */
  1241. static void recvit(cmd, type)
  1242.     char *cmd;
  1243.     int type;
  1244. {
  1245.     int mode;
  1246.     opt_t opts;
  1247.     off_t size;
  1248.     time_t mtime, atime;
  1249.     char *owner, *group, *file;
  1250.     char new[MAXPATHLEN];
  1251.     int freespace = -1, freefiles = -1;
  1252.     char *cp = cmd;
  1253.  
  1254.     /*
  1255.      * Get rdist option flags
  1256.      */
  1257.     opts = strtol(cp, &cp, 8);
  1258.     if (*cp++ != ' ') {
  1259.         error("recvit: options not delimited");
  1260.         return;
  1261.     }
  1262.  
  1263.     /*
  1264.      * Get file mode
  1265.      */
  1266.     mode = strtol(cp, &cp, 8);
  1267.     if (*cp++ != ' ') {
  1268.         error("recvit: mode not delimited");
  1269.         return;
  1270.     }
  1271.  
  1272.     /*
  1273.      * Get file size
  1274.      */
  1275.     size = strtol(cp, &cp, 10);
  1276.     if (*cp++ != ' ') {
  1277.         error("recvit: size not delimited");
  1278.         return;
  1279.     }
  1280.  
  1281.     /*
  1282.      * Get modification time
  1283.      */
  1284.     mtime = strtol(cp, &cp, 10);
  1285.     if (*cp++ != ' ') {
  1286.         error("recvit: mtime not delimited");
  1287.         return;
  1288.     }
  1289.  
  1290.     /*
  1291.      * Get access time
  1292.      */
  1293.     atime = strtol(cp, &cp, 10);
  1294.     if (*cp++ != ' ') {
  1295.         error("recvit: atime not delimited");
  1296.         return;
  1297.     }
  1298.  
  1299.     /*
  1300.      * Get file owner name
  1301.      */
  1302.     owner = strtok(cp, " ");
  1303.     if (owner == NULL) {
  1304.         error("recvit: owner name not delimited");
  1305.         return;
  1306.     }
  1307.  
  1308.     /*
  1309.      * Get file group name
  1310.      */
  1311.     group = strtok((char *)NULL, " ");
  1312.     if (group == NULL) {
  1313.         error("recvit: group name not delimited");
  1314.         return;
  1315.     }
  1316.  
  1317.     /*
  1318.      * Get file name.  Can't use strtok() since there could
  1319.      * be white space in the file name.
  1320.      */
  1321.     file = group + strlen(group) + 1;
  1322.     if (file == NULL) {
  1323.         error("recvit: no file name");
  1324.         return;
  1325.     }
  1326.  
  1327.     debugmsg(DM_MISC,
  1328.          "recvit: opts = %04o mode = %04o size = %d mtime = %d",
  1329.          opts, mode, size, mtime);
  1330.     debugmsg(DM_MISC,
  1331.        "recvit: owner = '%s' group = '%s' file = '%s' catname = %d isdir = %d",
  1332.          owner, group, file, catname, (type == S_IFDIR) ? 1 : 0);
  1333.  
  1334.     if (type == S_IFDIR) {
  1335.         if (catname >= sizeof(sptarget)) {
  1336.             error("%s: too many directory levels", target);
  1337.             return;
  1338.         }
  1339.         sptarget[catname] = ptarget;
  1340.         if (catname++) {
  1341.             *ptarget++ = '/';
  1342.             while (*ptarget++ = *file++)
  1343.                 ;
  1344.             ptarget--;
  1345.         }
  1346.     } else {
  1347.         /*
  1348.          * Create name of temporary file
  1349.          */
  1350.         if (catname && cattarget(file) < 0) {
  1351.             error("Cannot set file name.");
  1352.             return;
  1353.         }
  1354.         file = strrchr(target, '/');
  1355.         if (file == NULL)
  1356.             (void) strcpy(new, tempname);
  1357.         else if (file == target)
  1358.             (void) sprintf(new, "/%s", tempname);
  1359.         else {
  1360.             *file = CNULL;
  1361.             (void) sprintf(new, "%s/%s", target, tempname);
  1362.             *file = '/';
  1363.         }
  1364.         (void) mktemp(new);
  1365.     }
  1366.  
  1367.     /*
  1368.      * Check to see if there is enough free space and inodes
  1369.      * to install this file.
  1370.      */
  1371.     if (min_freespace || min_freefiles) {
  1372.         /* Convert file size to kilobytes */
  1373.         int fsize = size / 1024;
  1374.  
  1375.         if (getfilesysinfo(target, &freespace, &freefiles) != 0)
  1376.             return;
  1377.  
  1378.         /*
  1379.          * filesystem values < 0 indicate unsupported or unavailable
  1380.          * information.
  1381.          */
  1382.         if (min_freespace && (freespace >= 0) && 
  1383.             (freespace - fsize < min_freespace)) {
  1384.             error(
  1385.              "%s: Not enough free space on filesystem: min %d free %d",
  1386.                   target, min_freespace, freespace);
  1387.             return;
  1388.         }
  1389.         if (min_freefiles && (freefiles >= 0) &&
  1390.             (freefiles - 1 < min_freefiles)) {
  1391.             error(
  1392.              "%s: Not enough free files on filesystem: min %d free %d",
  1393.                   target, min_freefiles, freefiles);
  1394.             return;
  1395.         }
  1396.     }
  1397.  
  1398.     /*
  1399.      * Call appropriate receive function to receive file
  1400.      */
  1401.     switch (type) {
  1402.     case S_IFDIR:
  1403.         recvdir(opts, mode, owner, group);
  1404.         break;
  1405.  
  1406.     case S_IFLNK:
  1407.         recvlink(new, opts, mode, size);
  1408.         break;
  1409.  
  1410.     case S_IFREG:
  1411.         recvfile(new, opts, mode, owner, group, mtime, atime, size);
  1412.         break;
  1413.  
  1414.     default:
  1415.         error("%d: unknown file type", type);
  1416.         break;
  1417.     }
  1418. }
  1419.  
  1420. /*
  1421.  * Set target information
  1422.  */
  1423. static void settarget(cmd, isdir)
  1424.     char *cmd;
  1425.     int isdir;
  1426. {
  1427.     char *cp = cmd;
  1428.     opt_t opts;
  1429.  
  1430.     catname = isdir;
  1431.  
  1432.     /*
  1433.      * Parse options for this target
  1434.      */
  1435.     opts = strtol(cp, &cp, 8);
  1436.     if (*cp++ != ' ') {
  1437.         error("settarget: options not delimited");
  1438.         return;
  1439.     }
  1440.     options = opts;
  1441.  
  1442.     /*
  1443.      * Handle target
  1444.      */
  1445.     if (exptilde(target, cp) == NULL)
  1446.         return;
  1447.     ptarget = target;
  1448.     while (*ptarget)
  1449.         ptarget++;
  1450.  
  1451.     ack();
  1452. }
  1453.  
  1454. /*
  1455.  * Cleanup in preparation for exiting.
  1456.  */
  1457. extern void cleanup()
  1458. {
  1459.     /* We don't need to do anything */
  1460. }
  1461.  
  1462. /*
  1463.  * Server routine to read requests and process them.
  1464.  */
  1465. extern void server()
  1466. {
  1467.     static char cmdbuf[BUFSIZ];
  1468.     register char *cp;
  1469.     register int n;
  1470.     extern jmp_buf finish_jmpbuf;
  1471.  
  1472.     if (setjmp(finish_jmpbuf))
  1473.         return;
  1474.     (void) signal(SIGHUP, sighandler);
  1475.     (void) signal(SIGINT, sighandler);
  1476.     (void) signal(SIGQUIT, sighandler);
  1477.     (void) signal(SIGTERM, sighandler);
  1478.     (void) signal(SIGPIPE, sighandler);
  1479.     (void) umask(oumask = umask(0));
  1480.     (void) strcpy(tempname, _RDIST_TMP);
  1481.     if (fromhost) {
  1482.         message(MT_SYSLOG, "Startup for %s", fromhost);
  1483. #if     defined(SETARGS)
  1484.         setproctitle("Serving %s", fromhost);
  1485. #endif    /* SETARGS */
  1486.     }
  1487.  
  1488.     /* 
  1489.      * Let client know we want it to send it's version number
  1490.      */
  1491.     (void) sendcmd(S_VERSION, NULL);
  1492.  
  1493.     if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) {
  1494.         error("server: expected control record");
  1495.         return;
  1496.     }
  1497.  
  1498.     if (cmdbuf[0] != S_VERSION || !isdigit(cmdbuf[1])) {
  1499.         error("Expected version command, received: \"%s\".", cmdbuf);
  1500.         return;
  1501.     }
  1502.  
  1503.     proto_version = atoi(&cmdbuf[1]);
  1504.     if (proto_version != VERSION) {
  1505.         error("Protocol version %d is not supported.", proto_version);
  1506.         return;
  1507.     }
  1508.  
  1509.     /* Version number is okay */
  1510.     ack();
  1511.  
  1512.     /*
  1513.      * Main command loop
  1514.      */
  1515.     for ( ; ; ) {
  1516.         n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE);
  1517.         if (n == -1)        /* EOF */
  1518.             return;
  1519.         if (n == 0) {
  1520.             error("server: expected control record");
  1521.             continue;
  1522.         }
  1523.  
  1524.         switch (*cp++) {
  1525.         case C_SETCONFIG:      /* Configuration info */
  1526.                 setconfig(cp);
  1527.             ack();
  1528.             continue;
  1529.  
  1530.         case C_DIRTARGET:      /* init target file/directory name */
  1531.             settarget(cp, TRUE);
  1532.             continue;
  1533.  
  1534.         case C_TARGET:      /* init target file/directory name */
  1535.             settarget(cp, FALSE);
  1536.             continue;
  1537.  
  1538.         case C_RECVREG:      /* Transfer a regular file. */
  1539.             recvit(cp, S_IFREG);
  1540.             continue;
  1541.  
  1542.         case C_RECVDIR:      /* Transfer a directory. */
  1543.             recvit(cp, S_IFDIR);
  1544.             continue;
  1545.  
  1546.         case C_RECVSYMLINK:      /* Transfer symbolic link. */
  1547.             recvit(cp, S_IFLNK);
  1548.             continue;
  1549.  
  1550.         case C_RECVHARDLINK:      /* Transfer hard link. */
  1551.             hardlink(cp);
  1552.             continue;
  1553.  
  1554.         case C_END:          /* End of transfer */
  1555.             *ptarget = CNULL;
  1556.             if (catname <= 0) {
  1557.                 error("server: too many '%c's", C_END);
  1558.                 continue;
  1559.             }
  1560.             ptarget = sptarget[--catname];
  1561.             *ptarget = CNULL;
  1562.             ack();
  1563.             continue;
  1564.  
  1565.         case C_CLEAN:          /* Clean. Cleanup a directory */
  1566.             clean(cp);
  1567.             continue;
  1568.  
  1569.         case C_QUERY:          /* Query file/directory */
  1570.             query(cp);
  1571.             continue;
  1572.  
  1573.         case C_SPECIAL:      /* Special. Execute commands */
  1574.             dospecial(cp);
  1575.             continue;
  1576.  
  1577.         case C_CMDSPECIAL:      /* Cmd Special. Execute commands */
  1578.             docmdspecial();
  1579.             continue;
  1580.  
  1581. #ifdef DOCHMOD
  1582.             case C_CHMOD:          /* Set mode */
  1583.             dochmod(cp);
  1584.             continue;
  1585. #endif /* DOCHMOD */
  1586.  
  1587.         case C_ERRMSG:        /* Normal error message */
  1588.             if (cp && *cp)
  1589.                 message(MT_NERROR|MT_NOREMOTE, "%s", cp);
  1590.             continue;
  1591.  
  1592.         case C_FERRMSG:        /* Fatal error message */
  1593.             if (cp && *cp)
  1594.                 message(MT_FERROR|MT_NOREMOTE, "%s", cp);
  1595.             return;
  1596.  
  1597.         default:
  1598.             error("server: unknown command '%s'", cp - 1);
  1599.         case CNULL:
  1600.             continue;
  1601.         }
  1602.     }
  1603. }
  1604.